package references

import (
	"JVM/Jvmgo/GoToJVM/instructions/base"
	"JVM/Jvmgo/GoToJVM/runtimeData"
	"JVM/Jvmgo/GoToJVM/runtimeData/heap"
)

/*
	primitiveArrayInfo(4,  0'Z, boolean, int).
	primitiveArrayInfo(5,  0'C, char,    int).
	primitiveArrayInfo(6,  0'F, float,   float).
	primitiveArrayInfo(7,  0'D, double,  double).
	primitiveArrayInfo(8,  0'B, byte,    int).
	primitiveArrayInfo(9,  0'S, short,   int).
	primitiveArrayInfo(10, 0'I, int,     int).
	primitiveArrayInfo(11, 0'J, long,    long).
*/

const (
	AT_BOOLEAN = 4
	AT_CHAR    = 5
	AT_FLOAT   = 6
	AT_DOUBLE  = 7
	AT_BYTE    = 8
	AT_SHORT   = 9
	AT_INT     = 10
	AT_LONG    = 11
)

type NEW_ARRAY struct {
	atype uint8
}

func (self *NEW_ARRAY) FetchOperands(reader *base.BytecodeReader) {
	self.atype = reader.ReadUint8()
}

/*
数组类：
	数组类指的是描述数组类型的类。在Java中，数组类是一种特殊的类，用于表示数组类型。数组类本身是一个类对象，它包含有关数组类型的信息，例如数组的维度、元素类型等。
	数组类是在运行时动态创建的，每种数组类型都有对应的数组类。Java中的数组类都是继承自java.lang.Object类的。
	数组类的创建是由Java虚拟机（JVM）在运行时动态完成的，程序员无法直接定义或实例化数组类。

数组对象：
	数组对象是实际存储数据的实例。当程序创建一个数组时，会在堆内存中分配一块连续的内存空间用于存储数组元素。
	数组对象包含了数组的长度和元素，可以通过索引访问数组中的元素。
	数组对象是数组类的实例，它的类型是数组类的类型。
*/

/*
 1. 在Java中，创建数组对象之前必须加载对应类型的数组类，
    这是因为数组在Java中是一种特殊的对象，它的类型信息由数组类来描述。
 2. 当我们在Java程序中创建一个数组对象时，例如int[] arr = new int[5];，
    实际上是在运行时动态创建了一个数组对象，
    并且需要加载对应类型的数组类来描述这个数组对象的类型信息。
    这个数组类包含了有关数组的维度、元素类型等信息。
*/
func (self *NEW_ARRAY) Execute(frame *runtimeData.Frame) {
	stack := frame.OperandStack()
	// 第二个操作数是count,为数组长度
	count := stack.PopInt()
	if count < 0 {
		// 数组的长度不小于零
		panic("java.lang.NegativeArraySizeException")
	}

	classLoader := frame.Method().Class().Loader()
	// 加载数组类
	arrClass := getPrimitiveArrayClass(classLoader, self.atype)
	// 新建数组对象
	arr := arrClass.NewArray(uint(count))
	// 将对象指针推到操作数栈
	stack.PushRef(arr)
}

// 基础类型数组类
func getPrimitiveArrayClass(loader *heap.ClassLoader, atype uint8) *heap.Class {
	switch atype {
	case AT_BOOLEAN:
		return loader.LoadClass("[Z")
	case AT_BYTE:
		return loader.LoadClass("[B")
	case AT_CHAR:
		return loader.LoadClass("[C")
	case AT_SHORT:
		return loader.LoadClass("[S")
	case AT_INT:
		return loader.LoadClass("[I")
	case AT_LONG:
		return loader.LoadClass("[J")
	case AT_FLOAT:
		return loader.LoadClass("[F")
	case AT_DOUBLE:
		return loader.LoadClass("[D")
	default:
		panic("Invalid atype!")
	}
}
